#include <linux/module.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/slab.h>

#define BUF_SIZE 	64

struct c0_dev {
	struct device *dev_c0;
	char buf[BUF_SIZE];
};
struct c0_dev *c0;


int c0_open(struct inode *inode, struct file *file) {
	int major = MAJOR(inode->i_rdev);
	int minor = MINOR(inode->i_rdev);
	printk("%s major : %d, minor : %d\n", __func__, major, minor);
	return 0;
}

int c0_close(struct inode *inode, struct file *file) {
	printk("%s\n", __func__);
	return 0;
}

ssize_t c0_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos) {
	int readed_size, usable_size, free_size;
	int ret;

	free_size = BUF_SIZE - *ppos;
	usable_size = free_size > count ? count : free_size;
	if (usable_size == 0) {
		dev_warn(c0->dev_c0, "no space read");
		return -ENOSPC;
	}
	dev_info(c0->dev_c0, "%s: count %d, free %d, use %d\n", __func__, count, free_size, usable_size);

	ret = copy_to_user(buf, c0->buf + *ppos, usable_size);
	if(ret == usable_size) // not copy usable_size byte; ret == 0 copy ok
		return -EFAULT;

	readed_size = usable_size - ret;
	*ppos += readed_size;

	dev_info(c0->dev_c0, "readed_size = %d , pos = %lld \n", readed_size, *ppos);

	return readed_size;     //会连续读。
	/* return 0; */  //如果返回0，cat节点不会有打印
}

ssize_t c0_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos) {
	int written_size, usable_size, free_size;
	int ret;

	free_size = BUF_SIZE - *ppos;
	usable_size = free_size > count ? count : free_size;
	if (usable_size == 0) {
		dev_warn(c0->dev_c0, "no space write");
		return -ENOSPC;
	}
	dev_info(c0->dev_c0, "%s: count %d, free %d, use %d\n", __func__, count, free_size, usable_size);

	memset(c0->buf, 0, BUF_SIZE);
	ret = copy_from_user(c0->buf + *ppos, buf, usable_size);
	if(ret == usable_size)
		return -1;

	written_size = usable_size - ret;
	*ppos += written_size;

	dev_info(c0->dev_c0, "write size: %d , pos: %lld \n", written_size, *ppos);

	return written_size;
	/* return 0; */ 
}

const struct file_operations c0_fops = {
	.open    = c0_open,
	.read    = c0_read,
	.write   = c0_write,
	.release = c0_close,
};

struct miscdevice misc_c0 = {
	.minor = MISC_DYNAMIC_MINOR,
	.name  = "misc_c0",
	.fops  = &c0_fops, 
};

static int __init dev_init(void) {
	int ret;
	c0 = (struct c0_dev*)kmalloc(sizeof(struct c0_dev), GFP_KERNEL);
	ret = misc_register(&misc_c0);
	if (ret) {
		dev_info(c0->dev_c0, "misc register failed\n");
		return ret;
	}
	c0->dev_c0 = misc_c0.this_device;
	memset(c0->buf, 0, BUF_SIZE);
	dev_info(c0->dev_c0, "register device misc_c0\n");
	return 0;

}

static void __exit dev_exit(void) {
	printk("----%s----\n", __func__);
	misc_deregister(&misc_c0);
	kfree(c0);
}

module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");